Add rest of garmin_fit reference files.
authorrobertlipe@gmail.com <robertlipe@gmail.com@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Tue, 26 Jul 2011 15:13:33 +0000 (15:13 +0000)
committerrobertlipe@gmail.com <robertlipe@gmail.com@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Tue, 26 Jul 2011 15:13:33 +0000 (15:13 +0000)
git-svn-id: http://gpsbabel.googlecode.com/svn/trunk@4080 f51c46e8-681c-474f-0cfe-069cfd0219fb

gpsbabel/garmin_fit.c [new file with mode: 0644]
gpsbabel/reference/track/fit-sample.fit [new file with mode: 0644]
gpsbabel/testo.d/garmin_fit.test

diff --git a/gpsbabel/garmin_fit.c b/gpsbabel/garmin_fit.c
new file mode 100644 (file)
index 0000000..146972f
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+
+    Support for FIT track files.
+
+    Copyright (C) 2011 Paul Brook, paul@nowt.org
+    Copyright (C) 2003-2011  Robert Lipe, robertlipe@gpsbabel.org
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+
+#include "defs.h"
+#include <ctype.h>
+
+#define MYNAME "fit"
+
+
+static
+arglist_t fit_args[] = {
+  ARG_TERMINATOR
+};
+
+typedef struct {
+  int id;
+  int size;
+  int type;
+} fit_field_t;
+
+typedef struct {
+  int endian;
+  int global_id;
+  int num_fields;
+  fit_field_t* fields;
+} fit_message_def;
+
+static struct {
+  int len;
+  int endian;
+  route_head* track;
+  gbuint32 last_timestamp;
+  fit_message_def message_def[16];
+} fit_data;
+
+static gbfile* fin;
+
+/*******************************************************************************
+* %%%        global callbacks called by gpsbabel main process              %%% *
+*******************************************************************************/
+
+static void
+fit_rd_init(const char* fname)
+{
+  fin = gbfopen_le(fname, "rb", MYNAME);
+}
+
+static void
+fit_rd_deinit(void)
+{
+  gbfclose(fin);
+}
+
+static void
+fit_parse_header(void)
+{
+  int len;
+  int ver;
+  char sig[4];
+
+  len = gbfgetc(fin);
+  if (len == EOF || len < 12) {
+    fatal(MYNAME ": Bad header\n");
+  }
+  ver = gbfgetc(fin);
+  if (ver == EOF || (ver >> 4) > 1)
+    fatal(MYNAME ": Unsupported protocol version %d.%d\n",
+          ver >> 4, ver & 0xf);
+  // profile version
+  ver = gbfgetuint16(fin);
+  // data length
+  fit_data.len = gbfgetuint32(fin);
+  // File signature
+  is_fatal(gbfread(sig, 4, 1, fin) != 1,
+           MYNAME ": Unexpected end of file\n");
+  if (sig[0] != '.' || sig[1] != 'F' || sig[2] != 'I' || sig[3] != 'T') {
+    fatal(MYNAME ": .FIT signature missing\n");
+  }
+}
+
+static gbuint8
+fit_getuint8(void)
+{
+  int val;
+
+  if (fit_data.len == 0) {
+    fatal(MYNAME ": record truncated\n");
+  }
+  val = gbfgetc(fin);
+  if (val == EOF) {
+    fatal(MYNAME ": unexpected end of file\n");
+  }
+  fit_data.len--;
+  return (gbuint8)val;
+
+}
+
+static gbuint16
+fit_getuint16(void)
+{
+  char buf[2];
+
+  if (fit_data.len < 2) {
+    fatal(MYNAME ": record truncated\n");
+  }
+  is_fatal(gbfread(buf, 2, 1, fin) != 1,
+           MYNAME ": unexpected end of file\n");
+  fit_data.len -= 2;
+  if (fit_data.endian) {
+    return be_read16(buf);
+  } else {
+    return le_read16(buf);
+  }
+
+}
+
+static gbuint32
+fit_getuint32(void)
+{
+  char buf[4];
+
+  if (fit_data.len < 4) {
+    fatal(MYNAME ": record truncated\n");
+  }
+  is_fatal(gbfread(buf, 4, 1, fin) != 1,
+           MYNAME ": unexpected end of file\n");
+  fit_data.len -= 4;
+  if (fit_data.endian) {
+    return be_read32(buf);
+  } else {
+    return le_read32(buf);
+  }
+
+}
+
+static void
+fit_parse_definition_message(gbuint8 header)
+{
+  int local_id = header & 0x1f;
+  fit_message_def* def = &fit_data.message_def[local_id];
+  int i;
+
+  if (def->fields) {
+    free(def->fields);
+  }
+
+  is_fatal(fit_getuint8() != 0,
+           MYNAME ": Definition message reserved bits not zero\n");
+  def->endian = fit_getuint8();
+  if (def->endian > 1) {
+    fatal(MYNAME ": Bad endian field\n");
+  }
+  fit_data.endian = def->endian;
+  def->global_id = fit_getuint16();
+  def->num_fields = fit_getuint8();
+  if (def->num_fields == 0) {
+    def->fields = (fit_field_t*) xmalloc(sizeof(fit_field_t));
+    return;
+  }
+  def->fields = (fit_field_t*) xmalloc(def->num_fields * sizeof(fit_field_t));
+  for (i = 0; i < def->num_fields; i++) {
+    def->fields[i].id = fit_getuint8();
+    def->fields[i].size = fit_getuint8();
+    def->fields[i].type = fit_getuint8();
+  }
+}
+
+static gbuint32
+fit_read_field(fit_field_t* f)
+{
+  int i;
+
+  switch (f->type) {
+  case 1: // sint8
+  case 2: // uint8
+    is_fatal(f->size != 1,
+             MYNAME ": Bad field size in data message\n");
+    return fit_getuint8();
+  case 0x83: // sint16
+  case 0x84: // uint16
+    is_fatal(f->size != 2,
+             MYNAME ": Bad field size in data message\n");
+    return fit_getuint16();
+  case 0x85: // sint32
+  case 0x86: // uint32
+    is_fatal(f->size != 4,
+             MYNAME ": Bad field size in data message\n");
+    return fit_getuint32();
+  default: // Ignore everything else for now.
+    for (i = 0; i < f->size; i++) {
+      fit_getuint8();
+    }
+    return -1;
+  }
+}
+
+static void
+fit_parse_data(fit_message_def* def, int time_offset)
+{
+  fit_field_t* f;
+  gbuint32 timestamp = fit_data.last_timestamp + time_offset;
+  gbuint32 val;
+  gbint32 lat = 0x7fffffff;
+  gbint32 lon = 0x7fffffff;
+  gbuint16 alt = 0xffff;
+  gbuint16 speed = 0xffff;
+  gbuint8 heartrate = 0xff;
+  gbuint8 cadence = 0xff;
+  gbuint16 power = 0xffff;
+  gbint8 temperature = 0x7f;
+  int i;
+  waypoint* waypt;
+
+  for (i = 0; i < def->num_fields; i++) {
+    f = &def->fields[i];
+    val = fit_read_field(f);
+    if (f->id == 253) {
+      fit_data.last_timestamp = timestamp = val;
+    } else {
+      switch (def->global_id) {
+      case 20: // record mesage
+        switch (f->id) {
+        case 0:
+          lat = val;
+          break;
+        case 1:
+          lon = val;
+          break;
+        case 2:
+          alt = val;
+          break;
+        case 3:
+          heartrate = val;
+          break;
+        case 4:
+          cadence = val;
+          break;
+        case 6:
+          speed = val;
+          break;
+        case 7:
+          power = val;
+          break;
+        case 13:
+          temperature = val;
+          break;
+        }
+      }
+    }
+  }
+  switch (def->global_id) {
+  case 20: // record mesage
+    if (lat == 0x7fffffff || lon == 0x7fffffff) {
+      break;
+    }
+
+    waypt = waypt_new();
+    waypt->latitude = (lat / (float)0x7fffffff) * 180;
+    waypt->longitude = (lon / (float)0x7fffffff) * 180;
+    if (alt != 0xffff) {
+      waypt->altitude = (alt / 5.0) - 500;
+    }
+    waypt->creation_time = timestamp + 631065600;
+    if (speed != 0xffff) {
+      WAYPT_SET(waypt, speed, speed / 1000.0f);
+    }
+    if (heartrate != 0xff) {
+      waypt->heartrate = heartrate;
+    }
+    if (cadence != 0xff) {
+      waypt->cadence = cadence;
+    }
+    if (power != 0xffff) {
+      waypt->power = power;
+    }
+    if (temperature != 0x7f) {
+      WAYPT_SET(waypt, temperature, temperature);
+    }
+    track_add_wpt(fit_data.track, waypt);
+    break;
+  }
+}
+
+static void
+fit_parse_data_message(gbuint8 header)
+{
+  int local_id = header & 0x1f;
+  fit_message_def* def = &fit_data.message_def[local_id];
+  fit_parse_data(def, 0);
+}
+
+static void
+fit_parse_compressed_message(gbuint8 header)
+{
+  int local_id = (header >> 5) & 3;
+  fit_message_def* def = &fit_data.message_def[local_id];
+  fit_parse_data(def, header & 0x1f);
+}
+
+static void
+fit_parse_record(void)
+{
+  gbuint8 header;
+
+  header = fit_getuint8();
+  if (header & 0x80) {
+    fit_parse_compressed_message(header);
+  } else if (header & 0x40) {
+    fit_parse_definition_message(header);
+  } else {
+    fit_parse_data_message(header);
+  }
+}
+
+static void
+fit_read(void)
+{
+  fit_parse_header();
+
+  fit_data.track = route_head_alloc();
+  track_add_head(fit_data.track);
+  while (fit_data.len) {
+    fit_parse_record();
+  }
+}
+
+/**************************************************************************/
+
+// capabilities below means: we can only read and write waypoints
+// please change this depending on your new module
+
+ff_vecs_t format_fit_vecs = {
+  ff_type_file,
+  {
+    ff_cap_none                        /* waypoints */,
+    ff_cap_read                /* tracks */,
+    ff_cap_none                /* routes */
+  },
+  fit_rd_init,
+  NULL,
+  fit_rd_deinit,
+  NULL,
+  fit_read,
+  NULL,
+  NULL,
+  fit_args,
+  CET_CHARSET_ASCII, 0         /* ascii is the expected character set */
+  /* not fixed, can be changed through command line parameter */
+};
+/**************************************************************************/
diff --git a/gpsbabel/reference/track/fit-sample.fit b/gpsbabel/reference/track/fit-sample.fit
new file mode 100644 (file)
index 0000000..5f797e9
Binary files /dev/null and b/gpsbabel/reference/track/fit-sample.fit differ
index faba8002ef6bd2c5a3161785ce7a10a0b0146792..65a4c40adfcca0064e939a52b586a0c400f87011 100644 (file)
@@ -2,6 +2,5 @@
 # Basic FIT tests (readonly)
 #
 rm -f ${TMPDIR}/fit-*
-echo Skipping garmin_fit until Paul Brook submits test file.
-# gpsbabel -i garmin_fit -f ${REFERENCE}/track/fit-sample.fit -o gpx -F ${TMPDIR}/fit-sameple.gpx
-#compare ${TMPDIR}/fit-sameple.gpx ${REFERENCE}/track/fit-sample.gpx
+gpsbabel -i garmin_fit -f ${REFERENCE}/track/fit-sample.fit -o gpx -F ${TMPDIR}/fit-sameple.gpx
+compare ${TMPDIR}/fit-sameple.gpx ${REFERENCE}/track/fit-sample.gpx